package com.boom.springapp.javafxdemo.utils;

/**
 * @author hhl
 * @version 1.0
 * @description
 * @date 2025/11/20 22:40
 */

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class CryptoUtils {

    // 加密表
    private static final int[] MIXIN_KEY_ENC_TAB = {
            46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
            33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
            61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
            36, 20, 34, 44, 52
    };

    // BV转AV常量
    private static final long XOR_CODE = 23442827791579L;
    private static final long MAX_CODE = 2251799813685247L;
    private static final String CHARTS = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf";

    // 缓存
    private static final Map<String, String> cache = new HashMap<>();
    private static long lastUpdateTime = 0;

    /**
     * 签名并生成新的URL
     */
    public static String signAndGenerateUrl(String urlStr, String cookie) {
        try {
            URL url = new URL(urlStr);
            Map<String, String> params = parseQueryString(url.getQuery());

            // 获取加密密钥
            String[] keys = getWbiKeysCached(cookie);
            String imgKey = keys[0];
            String subKey = keys[1];

            // 加密参数
            Map<String, String> newParams = encWbi(params, imgKey, subKey);

            // 构建新的查询字符串
            String newQuery = buildQueryString(newParams);

            // 构建新的URL
            String newUrl = new StringBuilder()
                    .append(url.getProtocol()).append("://")
                    .append(url.getHost())
                    .append(url.getPath())
                    .append("?").append(newQuery)
                    .toString();

            return newUrl;
        } catch (Exception e) {
            log.error("签名URL失败: " + e.getMessage());
            return urlStr;
        }
    }

    /**
     * WBI加密
     */
    public static Map<String, String> encWbi(Map<String, String> params, String imgKey, String subKey) {
        // 获取混合密钥
        String mixinKey = getMixinKey(imgKey + subKey);

        // 添加时间戳
        String currTime = String.valueOf(System.currentTimeMillis() / 1000);
        params.put("wts", currTime);

        // 排序并净化字符串
        Map<String, String> sortedParams = params.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        e -> sanitizeString(e.getValue()),
                        (e1, e2) -> e1,
                        LinkedHashMap::new
                ));

        // 构建查询字符串
        String queryStr = buildQueryString(sortedParams);

        // 计算w_rid
        String wRid = md5(queryStr + mixinKey);
        sortedParams.put("w_rid", wRid);

        return sortedParams;
    }

    /**
     * 获取混合密钥
     */
    private static String getMixinKey(String orig) {
        StringBuilder result = new StringBuilder();
        for (int index : MIXIN_KEY_ENC_TAB) {
            if (index < orig.length()) {
                result.append(orig.charAt(index));
            }
        }
        return result.substring(0, Math.min(32, result.length()));
    }

    /**
     * 净化字符串
     */
    private static String sanitizeString(String s) {
        String[] unwantedChars = {"!", "'", "(", ")", "*"};
        for (String charToRemove : unwantedChars) {
            s = s.replace(charToRemove, "");
        }
        return s;
    }

    /**
     * 更新缓存
     */
    private static void updateCache(String cookie) {
        if (System.currentTimeMillis() - lastUpdateTime < 600000) { // 10分钟
            return;
        }

        String[] keys = getWbiKeys(cookie);
        cache.put("img_key", keys[0]);
        cache.put("sub_key", keys[1]);
        lastUpdateTime = System.currentTimeMillis();
    }

    /**
     * 从缓存获取WBI密钥
     */
    public static String[] getWbiKeysCached(String cookie) {
        updateCache(cookie);
        return new String[]{
                cache.getOrDefault("img_key", ""),
                cache.getOrDefault("sub_key", "")
        };
    }

    /**
     * 获取WBI密钥
     */
    private static String[] getWbiKeys(String cookie) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet request = new HttpGet("https://api.bilibili.com/x/web-interface/nav");
            request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
            request.setHeader("Referer", "https://www.bilibili.com/");
            request.setHeader("Cookie", cookie);

            try (CloseableHttpResponse response = httpClient.execute(request)) {
                String responseBody = EntityUtils.toString(response.getEntity());
                ObjectMapper objectMapper = new ObjectMapper();
                JsonNode data = objectMapper.readTree(responseBody);

                String imgUrl = data.get("data").get("wbi_img").get("img_url").asText();
                String subUrl = data.get("data").get("wbi_img").get("sub_url").asText();

                String imgKey = imgUrl.substring(imgUrl.lastIndexOf("/") + 1, imgUrl.lastIndexOf("."));
                String subKey = subUrl.substring(subUrl.lastIndexOf("/") + 1, subUrl.lastIndexOf("."));

                return new String[]{imgKey, subKey};
            }
        } catch (Exception e) {
            log.error("获取WBI密钥失败: " + e.getMessage());
            return new String[]{"", ""};
        }
    }

    /**
     * 交换字符串中的两个字符
     */
    private static String swapString(String s, int x, int y) {
        char[] chars = s.toCharArray();
        char temp = chars[x];
        chars[x] = chars[y];
        chars[y] = temp;
        return new String(chars);
    }

    /**
     * BV号转AV号
     */
    public static long bvidToAvid(String bvid) {
        try {
            String s = swapString(swapString(bvid, 3, 9), 4, 7);
            String bv1 = s.substring(3);
            long temp = 0;

            for (char c : bv1.toCharArray()) {
                int idx = CHARTS.indexOf(c);
                if (idx == -1) { // 如果找不到字符
                    log.error("BV号转AV号失败: 无效字符 " + c);
                    return 0;
                }
                temp = temp * 58 + idx;
            }

            long avid = (temp & MAX_CODE) ^ XOR_CODE;
            log.info("BV号 " + bvid + " 转换为AV号 " + avid);
            return avid;
        } catch (Exception e) {
            log.error("BV号转AV号失败: " + e.getMessage());
            return 0;
        }
    }

    /**
     * AV号转BV号
     */
    public static String avidToBvid(long avid) {
        char[] arr = new char[12];
        arr[0] = 'B';
        arr[1] = 'V';
        arr[2] = '1';
        for (int i = 3; i < 12; i++) {
            arr[i] = '\0';
        }

        int bvIdx = 11;
        long temp = (avid | (MAX_CODE + 1)) ^ XOR_CODE;

        while (temp > 0) {
            int idx = (int)(temp % 58);
            arr[bvIdx] = CHARTS.charAt(idx);
            temp /= 58;
            bvIdx--;
        }

        String raw = new String(arr).replace("\0", "");
        return swapString(swapString(raw, 3, 9), 4, 7);
    }

    /**
     * MD5加密
     */
    private static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析查询字符串
     */
    private static Map<String, String> parseQueryString(String query) {
        Map<String, String> params = new HashMap<>();
        if (query == null || query.isEmpty()) {
            return params;
        }

        String[] pairs = query.split("&");
        for (String pair : pairs) {
            String[] keyValue = pair.split("=", 2);
            if (keyValue.length == 2) {
                try {
                    String key = URLDecoder.decode(keyValue[0], StandardCharsets.UTF_8.name());
                    String value = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8.name());
                    params.put(key, value);
                } catch (Exception e) {
                    log.warn("解析查询参数失败: " + pair);
                }
            }
        }
        return params;
    }

    /**
     * 构建查询字符串
     */
    private static String buildQueryString(Map<String, String> params) {
        return params.entrySet().stream()
                .map(entry -> {
                    try {
                        return URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()) +
                                "=" + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name());
                    } catch (Exception e) {
                        return "";
                    }
                })
                .filter(s -> !s.isEmpty())
                .collect(Collectors.joining("&"));
    }
}
